﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DigitalImage.softLib.util;
using System.Drawing;

namespace DigitalImage.softLib.stages._4RegionLabelling {
    class CRLregionLabelling:IRegionLabelling {
        private int numberOfRows;
        private int numberOfRowsTrain;
        private const int thrashold=5;

        public CRLregionLabelling(int numberOfRows, int numberOfRowsTrain) {
            this.numberOfRows = numberOfRows;
            this.numberOfRowsTrain = numberOfRowsTrain;
        }

        public List<List<CRasterRegion>> transform(byte[,] binaryImage,bool train) {
            int backupNumberOfRows = numberOfRows;
            if (train) {
                numberOfRows = numberOfRowsTrain;
            }
            binaryImage = (byte[,])binaryImage.Clone();
            List<CRasterRegion> regions = CImageUtil.regionLabeling(binaryImage);
            for(int i=0;i<regions.Count;i++) {
                if(regions[i].points.Count<thrashold) {
                    regions.RemoveAt(i);
                    i=i>0?i-1:i;
                }
            }
            regions=rotateRegions(regions);
            //ako ne zelim da proveravam da li je samo jedan red
            //List<List<CRasterRegion>> regionRows=splitInRowsKMeans(merge(regions));
            List<List<CRasterRegion>> regionRows =checkMultiLine(splitInRowsKMeans(merge(regions)));
            
            List<List<CRasterRegion>> result = new List<List<CRasterRegion>>();
            foreach (List<CRasterRegion> row in regionRows) {
                result.Add(findRowSpaces(row));
            }
            
            if (train) {
                numberOfRows = backupNumberOfRows;
            }

            return result;
        }

        //ispravi regione
        protected List<CRasterRegion> rotateRegions(List<CRasterRegion> regions) {
            foreach(CRasterRegion region in regions){
                List<Point> nPoints=new List<Point>();
                double ugao=Math.PI/2-Math.Abs(region.theta);
                foreach(Point p in region.points) {
                    double nX=Math.Cos(ugao)*(p.X-region.xM)
                        -Math.Sin(ugao)*(p.Y-region.yM)+region.xM;
                    double nY=Math.Sin(ugao)*(p.X-region.xM)
                        +Math.Cos(ugao)*(p.Y-region.yM)+region.yM;
                    nPoints.Add(new Point((int)nX, (int)nY));
                }
                region.points=nPoints;
                region.odrediSliku();
            }
            return regions;
        }
        
        protected List<List<CRasterRegion>> splitInRowsKMeans(List<CRasterRegion> regions) {
            List<List<CRasterRegion>> result = new List<List<CRasterRegion>>();

            CKMeansSpace kmeans = new CKMeansSpace();
            kmeans.brojGrupa = numberOfRows;
            List<CSpaceElement> spaceElements = new List<CSpaceElement>();
            for(int i=0;i<regions.Count;i++) { 
                spaceElements.Add(new CSpaceElement(regions[i].yM,i));
            }
            kmeans.elementi.AddRange(spaceElements);
            kmeans.podeliUGRupe(numberOfRows,0.0005);
            /*
            if (kmeans.grupe[0].elementi.Count != 33) {
                string output="";
                foreach (CClusterSpace group in kmeans.grupe) {
                    output+=(int)group.centar+"\n";
                    foreach (CSpaceElement spaceElement in group.elementi) {
                        output += (int)spaceElement.position + " ";
                    }
                    output += "\n";
                }
                btnCreateSimLib.printLog(output);
            }
            */
            List<CRasterRegion> row;
            foreach (CClusterSpace group in kmeans.grupe) {
                row =new List<CRasterRegion>();
                foreach(CSpaceElement spaceElement in group.elementi){
                    row.Add(regions[spaceElement.index]);
                }
                row.Sort();
                result.Add(row);
            }
            result.Sort(new CRegionListYComparation());
            return result;
        }

        protected List<List<CRasterRegion>> splitInRows(List<CRasterRegion> regions) {
            List<List<CRasterRegion>> result = new List<List<CRasterRegion>>();
            
            double maxHeight=Double.MinValue;
            double currentHeight;
            foreach(CRasterRegion region in regions){
                currentHeight=region.maxY-region.minY;
                if(currentHeight>maxHeight){
                    maxHeight=currentHeight;
                }
            }
            result.Add(new List<CRasterRegion>());
            result[0].Add(regions[0]);
            List<CRasterRegion> row;
            bool added;
            for (int i = 1; i < regions.Count;i++ ) {
                added = false;
                for (int j = 0; j < result.Count;j++ ) {
                    if (Math.Abs(result[j][0].yM - regions[i].yM) < maxHeight * 0.65) {
                        result[j].Add(regions[i]);
                        added = true;
                        break;
                    }
                }
                //nema slicne kolone
                if(!added){
                    row = new List<CRasterRegion>();
                    row.Add(regions[i]);
                    result.Add(row);
                }
            }
            //sort po x
            foreach (List<CRasterRegion> regionsList in result) {
                regionsList.Sort();
            }
            //sort po y
            result.Sort(new CRegionListYComparation());
            return result;
        }

        protected List<List<CRasterRegion>> checkMultiLine(List<List<CRasterRegion>> rows) {

            for(int i=0;i<rows.Count;i++) {
                for(int j=0;j<rows.Count;j++) {
                    if(i!=j){
                        bool sameRow=rows[i][0].minY<rows[j][0].yM && 
                                        rows[j][0].yM<rows[i][0].maxY;
                        if(sameRow) {
                            rows[i].AddRange(rows[j]);
                            rows.RemoveAt(j);
                            i=i>0?i-1:i;
                            j=j>0?j-1:j;
                        }
                    }
                }
            }

            return rows;
        }

        protected List<CRasterRegion> merge(List<CRasterRegion> regions) {
            CRasterRegion reg1;
            CRasterRegion reg2;
            int mergeThreashold=4;
            double maxHeight=Double.MinValue;
            double currentHeight;
            foreach(CRasterRegion region in regions){
                currentHeight=region.maxY-region.minY;
                if(currentHeight>maxHeight){
                    maxHeight=currentHeight;
                }
            }
            for(int i=0;i<regions.Count;i++) {
                for(int j=0;j<regions.Count;j++) {
                    if (i != j) {
                        reg1 = regions[i];
                        reg2 = regions[j];
                        bool mergeReady=((reg1.minX<=reg2.xM&&reg2.xM<=reg1.maxX)
                            ||Math.Abs(reg1.minX-reg2.maxX)<mergeThreashold
                            || (reg1.maxX>reg2.maxX && reg1.xM<reg2.xM))
                            &&(Math.Abs(reg1.yM-reg2.yM)<0.8*maxHeight);
                        if (mergeReady) {
                            reg1.points.AddRange(reg2.points);
                            reg1.odrediMomente();
                            regions.RemoveAt(j);
                            i = i > 0 ? i - 1 : i;
                            j = j > 0 ? j - 1 : j;
                        }
                    }
                }
            }
            return regions;
        }

        protected List<CRasterRegion> findRowSpaces(List<CRasterRegion> regions) {
            if (regions.Count <= 1) {
                return regions;
            }
            CKMeansSpace kmeans=new CKMeansSpace();
            List<CSpaceElement> spaceElements=new List<CSpaceElement>();
            for(int i=0;i<regions.Count-1;i++) {
                //ovo moze zato sto je niz sortiran
                spaceElements.Add(new CSpaceElement(Math.Abs(regions[i+1].minX-regions[i].maxX), i));
            }
            kmeans.elementi = spaceElements;
            kmeans.podeliUGRupe(2, 0.0005);

            double c1=kmeans.grupe[0].centar;
            double c2=kmeans.grupe[1].centar;
            double notSpace=c1<c2?c1:c2;
            double space=c1>=c2?c1:c2;
            double diff=0;

            double maxX=Double.MinValue;
            foreach (CRasterRegion region in regions) {
                diff=region.maxX-region.minX;
                if(diff>maxX){
                    maxX=diff;
                }
            }
            
            //radi samo ako se sadrzi vise od jedne reci
            List<CRasterRegion> newRegionsWithSpaces = new List<CRasterRegion>();
            diff*=0.4;
            if ((c1 > diff) || (c2 > diff)) {
                for (int i = 0; i < regions.Count - 1; i++) {
                    diff = Math.Abs(regions[i + 1].minX - regions[i].maxX);
                    newRegionsWithSpaces.Add(regions[i]);
                    if (Math.Abs(diff - notSpace) > Math.Abs(diff - space)) {
                        newRegionsWithSpaces.Add(CRasterRegion.spaceRegion);
                    }
                }
                //dodaj poslednji region
                newRegionsWithSpaces.Add(regions[regions.Count - 1]);
            } else {
                //ako je samo jedna rec
                newRegionsWithSpaces = regions;
            }

            return newRegionsWithSpaces;
        }
    
    }
}
